#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>

#include "main.h"
#include "shared_memory.h"
#include "socket.h"
#include "format.h"
#include "performConnection.h"

#define BUF 356                                         //vergrößert, da Piecelist > 256
#define BIGBUF 356                                      //bleibt testweise drinnen
#define Denken "THINKING\n"
#define MAX_DURCHLAEUFE 30
#define Version "VERSION 1.0"                           // TODO: in einer externen Datei festlegen
#define Warten "OKWAIT\n"


/* Klarnamen für Konstanten */
#define VERSIONSNUMMER_OFFSET 17
#define GROESSE_DES_VERSIONSNUMMER_RESTSTRINGS 40
#define GAMEKIND_OFFSET 9
#define GROESSE_DES_GAMEKIND_RESTSTRINGS 10
#define GAMENAME_OFFSET 1
#define GAMEOVER_OFFSET 11
#define GROESSE_DES_GAMENAME_RESTSTRINGS 2
#define CLIENT_NUMMER_OFFSET 6
#define CLIENT_NAME_OFFSET 8
#define SPIELER_ANZAHL_OFFSET 8
#define SPIELER_NUMMER_OFFSET 2
#define SPIELER_NAME_OFFSET 4
#define SPIELER_STATUS_OFFSET 2

/*
 * In diesem Unterprogramm wird die Prolog-Phase der Kommunikation realisiert.
 * Die Antworten des Servers werden geprüft, verarbeitet (tokenised) und gespeichert.
 */



int performConnection(int sock, char* GameID) {
        char *Buffer = (char*) malloc(sizeof(char)*BUF);
        char *Vergleich = (char*) malloc(sizeof(char)*BUF);
        char *Erste_Antwort = (char*) malloc(sizeof(char)*BUF);
        char *Zweite_Antwort = (char*) malloc(sizeof(char)*BUF);
        char *Erste_Zeile = (char*) malloc(sizeof(char)*BUF);
        char *Zweite_Zeile = (char*) malloc(sizeof(char)*BUF);
   
        int Abbruchzaehler = 0;

        void *Spielstein_Zeiger = shm_Segment_anbinden(Info_struct->shm_ID2);
            if (Spielstein_Zeiger == (void *) -1) {
                Aufraeumen();
                return EXIT_FAILURE;
            }
               
        /* Erste Antwort zusammenbauen. */
        strcpy(Erste_Antwort, Version);
        strcat(Erste_Antwort, "\n");
       
        /* Zweite Antwort zusammenbauen. */
        strcpy(Zweite_Antwort, "ID ");
        strcat(Zweite_Antwort, GameID);                                                                                        
        strcat(Zweite_Antwort, "\n");
       
       
                bzero(Vergleich, BUF);

                while (1) {
                               
                                bzero(Buffer, BUF);
                                recv(sock, Buffer,BUF,0);

                                /* Zusammenhängen der erhaltenen Teilstrings. */
                                strcat(Vergleich, Buffer);
                               
                                /* Abbruch falls ein '-' empfangen wird. */
                                if (strcspn(Buffer, "-") == 0 && strcspn(Vergleich, "\0") != 0) {
                                        printf("Folgender Fehler ist aufgetreten: ");
                                        printf("%s\n", Buffer);
                                        break;
                                }
                               

                                /*
                                 * Hier werden die Substrings nach folgendem Prinzip extrahiert:
                                 * 1. Wir wissen, wie lang die konstanten Anteile der Nachricht (msg) sind (im Folgenden "const_length").
                                 * 2. Wir koennen bestimmen, wie lang die Nachricht tatsaechlich ist (via strlen).
                                 * 3. Der zu extrahierende String ist dann "strlen(msg) - const_length" lang (auch fuer malloc relevant).
                                 * 4. Wir inkrementieren dann den Pointer, um die Zeichen vor dem zu extrahierenden String zu ueberspringen (const_skip);
                                 * 5. Wir wenden strncpy an, und zwar so: "strncpy(result, msg+const_skip, strlen(msg) - const_length)".
                                 * Dieses Prinzip funktioniert fuer einzelne Teilstrings beliebiger Laenge, laesst sich aber auch fuer mehrere Teilstrings
                                 * adaptieren.
                                 */


                                if ((strstr(Vergleich, "+ MNM Gameserver") != NULL) && (strstr(Vergleich, "accepting connections\n") != NULL)) {                
                                                int Versionsnummer_Groesse = strlen(Vergleich) - GROESSE_DES_VERSIONSNUMMER_RESTSTRINGS;
                                                char *Version_Server = (char*)malloc(Versionsnummer_Groesse*sizeof(char) + 1);
                                               
                                                bzero(Version_Server,5);
                                              
                                                /* Herausfiltern der Versionsnummer. */
                                                strncpy(Version_Server, Vergleich + VERSIONSNUMMER_OFFSET, Versionsnummer_Groesse);
                                                strcat(Version_Server, "\0");                                   // Wichtig, sonst wird der Output u.U. falsch, da String-Ende unbekannt.

                                                Gameserver_Version_ausgeben(Version_Server);

                                                send(sock,Erste_Antwort, 12, 0);
                                                bzero(Vergleich, BUF);
                                                free(Version_Server);
                                }
                               

                                if (strstr(Vergleich, "+ Client version accepted - please send Game-ID to join\n") != NULL) {
                                                send(sock, Zweite_Antwort, strlen(Zweite_Antwort), 0);
                                                bzero(Vergleich, BUF);                                          
                                }
                               

                                if ((strstr(Vergleich, "+ PLAYING") != NULL) && (strchr(Vergleich, '\n') != strrchr(Vergleich, '\n'))) {
                                                Erste_Zeile = strtok(Vergleich, "+");
                                                Zweite_Zeile = strtok(NULL, "+");
                                                int Gamekind_Groesse = strlen(Erste_Zeile) - GROESSE_DES_GAMEKIND_RESTSTRINGS;
                                                int Gamename_Groesse = strlen(Zweite_Zeile) - GROESSE_DES_GAMENAME_RESTSTRINGS;

                                                char *Gamekind = (char*)malloc(Gamekind_Groesse*sizeof(char) + 1);
                                                char *Gamename = (char*)malloc(Gamename_Groesse*sizeof(char) + 1);
                                               
                                                bzero(Gamekind,Gamekind_Groesse+1);
                                                bzero(Gamename,Gamename_Groesse+1);
                                               
                                                
                                                /* Herausfiltern des Gamekinds. */
                                                strncpy(Gamekind, Erste_Zeile + GAMEKIND_OFFSET, Gamekind_Groesse);
                                                strcat(Gamekind, "\0");

                                                /* Herausfiltern des Gamenamens. */
                                                strncpy(Gamename, Zweite_Zeile + GAMENAME_OFFSET, Gamename_Groesse);
                                                strcat(Gamename, "\0");
                                               
                                                strncpy(Info_struct->Spiel_Name, Gamename, 60);
                                                Gamekindname_ausgeben(Gamekind);
                                                Gamename_ausgeben(Gamename);

                                                if (strcmp(Gamekind,"NMMorris") != 0) {
                                                        printf("ERROR: Dieses Programm kann nur NMMorris\n");
                                                        break;
                                                }

                                                send(sock,"PLAYER\n", 7, 0);

                                                bzero(Vergleich, BUF);
                                }


                                if (strstr(Vergleich, "+ ENDPLAYERS") != NULL) {
                                        if(strstr(Vergleich, "+ ENDPIECELIST") == NULL){                                        // Wird das Programm nochmal auf eine bereits bestehende Game ID ausgeführt, so wird an dieser Stelle
                                                char *Bereit = (char*) malloc(sizeof(char)*BUF);                                // die komplette PIECELIST empfangen. Um die richtige Antwort an den Server zu senden der If-case
                                                char *Spieleranzahl = (char*) malloc(sizeof(char)*BUF);
                                                char *Spielername = (char*) malloc(sizeof(char)*BUF);
                                                char *Spielernummer = (char*) malloc(sizeof(char)*BUF);
                                                char *Teilstring = strtok(Vergleich, "\n");
                                               
                                                send(sock, Warten, strlen(Warten), 0);
                                                /* Herausfiltern von Spielername und Spielernummer des Clients. */
                                                strncpy(Spielernummer, Teilstring + CLIENT_NUMMER_OFFSET, 1);                                          
                                                strncpy(Spielername, Teilstring + CLIENT_NAME_OFFSET, 20);

                                                Info_struct->Spieler_ID = strtol(Spielernummer, NULL, 0);
                                                Client_Info_ausgeben(Spielernummer, Spielername);
                                                MeinSpielerNummer=atoi(Spielernummer);
                                                bzero(Spielernummer, BUF);
                                                bzero(Spielername, BUF);
                                               
                                                Teilstring = strtok(NULL, "\n");

                                                /* Herausfiltern der Spieleranzahl. */
                                                strncpy(Spieleranzahl, Teilstring + SPIELER_ANZAHL_OFFSET, 2);

                                                Info_struct->Spieler_Anzahl = strtol(Spieleranzahl, NULL, 0);
                                                Spieleranzahl_ausgeben(Spieleranzahl);
                                               
                                                Teilstring = strtok(NULL, "\n");

                                                int Laenge = strlen(Teilstring);

                                                /* Herausfiltern von Spielername und Spielernummer des Gegners. */
                                                strncpy(Spielernummer, Teilstring + SPIELER_NUMMER_OFFSET, 2);
                                                strncpy(Spielername, Teilstring + SPIELER_NAME_OFFSET, Laenge - 6);
                                                strncpy(Bereit, Teilstring - SPIELER_STATUS_OFFSET, 2);
                                                Anderer_Spieler_Info_ausgeben(Spielernummer, Spielername,Bereit);
                                               
                                                bzero(Vergleich, BUF);

                                                free(Bereit);
                                                free(Spieleranzahl);
                                                free(Spielername);
                                                free(Spielernummer);
                                                break;  
                                        }
                                        else if(strstr(Vergleich, "+ ENDPIECELIST") != NULL) {
                                                char *Bereit = (char*) malloc(sizeof(char)*BUF);           
                                                char *Spieleranzahl = (char*) malloc(sizeof(char)*BUF);
                                                char *Spielername = (char*) malloc(sizeof(char)*BUF);
                                                char *Spielernummer = (char*) malloc(sizeof(char)*BUF);
                                                char *Teilstring = strtok(Vergleich, "\n");
                                               
                                                /* Herausfiltern von Spielername und Spielernummer des Clients. */
                                                strncpy(Spielernummer, Teilstring + CLIENT_NUMMER_OFFSET, 1);                                          
                                                strncpy(Spielername, Teilstring + CLIENT_NAME_OFFSET, 20);

                                                Info_struct->Spieler_ID = strtol(Spielernummer, NULL, 0);
                                                Client_Info_ausgeben(Spielernummer, Spielername);
                                                MeinSpielerNummer=atoi(Spielernummer);
                                                bzero(Spielernummer, BUF);
                                                bzero(Spielername, BUF);
                                               
                                                Teilstring = strtok(NULL, "\n");

                                                /* Herausfiltern der Spieleranzahl. */
                                                strncpy(Spieleranzahl, Teilstring + SPIELER_ANZAHL_OFFSET, 2);

                                                Info_struct->Spieler_Anzahl = strtol(Spieleranzahl, NULL, 0);
                                                Spieleranzahl_ausgeben(Spieleranzahl);
                                               
                                                Teilstring = strtok(NULL, "\n");

                                                int Laenge = strlen(Teilstring);

                                                /* Herausfiltern von Spielername und Spielernummer des Gegners. */
                                                strncpy(Spielernummer, Teilstring + SPIELER_NUMMER_OFFSET, 2);
                                                strncpy(Spielername, Teilstring + SPIELER_NAME_OFFSET, Laenge - 6);
                                                strncpy(Bereit, Teilstring - SPIELER_STATUS_OFFSET, 2);
                                                Anderer_Spieler_Info_ausgeben(Spielernummer, Spielername,Bereit);
                                               
                                                bzero(Vergleich, BUF);

                                                free(Bereit);
                                                free(Spieleranzahl);
                                                free(Spielername);
                                                free(Spielernummer);

                                Spielstein_struct_Array = Spielstein_Zeiger;
   
                          char *Ausgabezeile = (char*) malloc(sizeof(char)* 356);  
                          char *Variable =  (char*) malloc(sizeof(char)*2);
                          char Sep1[] = "\n";
                          char Sep2[] = " \n";
                          int i = 0;                            
                         
                          bzero(Variable,3);
                          bzero(Ausgabezeile,BUF+1);
                         
                         
                          send(sock, Denken, strlen(Denken ), 0);
                         
                          // Auslesen der Zugezeit
                         strcpy(Ausgabezeile,Vergleich);                          
                         Ausgabezeile = strstr(Ausgabezeile,"MOVE");
                         Ausgabezeile = strtok(Ausgabezeile,Sep2);
                         Ausgabezeile = strtok(NULL,Sep2);
                         printf("Zugzeit: %s ms\n", Ausgabezeile);
                         
                         //Auslesen der schlagbaren Steine
                         strcpy(Ausgabezeile,Vergleich);
                         Ausgabezeile = strstr(Ausgabezeile,"CAPTURE");
                         Ausgabezeile = strtok(Ausgabezeile,Sep2);
                         Ausgabezeile = strtok(NULL,Sep2);
                         Info_struct->capture = (int) *Ausgabezeile - 48;
                         printf("Schlagbare Steine: %d\n", Info_struct->capture);
                         
//                       Auslesen der Anzahl der Spieler und Steine
                         strcpy(Ausgabezeile,Vergleich);
                         Ausgabezeile = strstr(Ausgabezeile,"PIECELIST ");
                         Ausgabezeile = strtok(Ausgabezeile,Sep2);
                         Ausgabezeile = strtok(NULL,Sep2);
                         printf("Anzahl Spieler, Anzahl Steine: %s\n", Ausgabezeile);
                         
//                       Auslesen aller Steinpositionen
                         for (i=0; i<18;i++) {
                         Ausgabezeile = strtok(NULL,Sep1);
                         
//                       Auslesen der Spielernummer
                         Variable = strncpy(Variable , Ausgabezeile + 7, 1);
                         Spielstein_struct_Array[i].Spielernummer = (int)*Variable-48;
                         bzero(Variable,2);
                         
//                       Auslesen der Steinposition
                         Variable = strncpy(Variable , Ausgabezeile + 11,strlen(Ausgabezeile)-11);                      
                         strcpy(Spielstein_struct_Array[i].Position,Variable);
                         Spielstein_struct_Array[i].Flag=1;
                         }
                         
                         free(Variable);
                         
                         bzero(Vergleich, BIGBUF);



                                        }
                                        else {
                                        send(sock, Denken, strlen(Denken ), 0);
                                        bzero(Vergleich, BUF);
                                        break;
                                        }
                                }
                        Abbruchzaehler++;
                       
                        if(Abbruchzaehler == MAX_DURCHLAEUFE){
                                        printf("ERROR: Fehler beim Vergleichen der Ausgabe des Servers");
                                        break;                                  
                                       
                        }
                }
               

               
                free(Buffer);
                free(Erste_Antwort);
                free(Zweite_Antwort);
                free(Vergleich);
                return 0;

}

    /* Durchführung des Spielphasenprotokolls
     *
     * In diesem Unterprogramm  wird, während das Spiel läuft, mit dem Server kommuniziert
     */

int Spielphase_durchfuehren(int sock, pid_t ppid) {

        char *Buffer = (char*) malloc(sizeof(char)*BIGBUF);
        char *Eingabe = (char*) malloc(sizeof(char)*6);
        char *Lesepuffer = malloc(sizeof(char)*20);
        char *Spielzug = (char*) malloc(sizeof(char)*100);
        char *Vergleich = (char*) malloc(sizeof(char)*BIGBUF);

        fd_set Filedeskriptoren;
       
        bzero(Eingabe, 100);
        bzero(Spielzug, 100);
        bzero(Vergleich, BIGBUF);

        void *Spielstein_Zeiger = shm_Segment_anbinden(Info_struct->shm_ID2);
            if (Spielstein_Zeiger == (void *) -1) {
                Aufraeumen();
                return EXIT_FAILURE;
            }
 
 
 
 
                while (1) {
                               
                                bzero(Buffer, BIGBUF);
                                recv(sock, Buffer,BIGBUF,0);

                                /* Zusammenhängen der erhaltenen Teilstrings. */
                                strcat(Vergleich, Buffer);

                                /* Abbruch falls ein '-' empfangen wird. */
                                if (strcspn(Buffer, "-") == 0 && strcspn(Vergleich, "\0") != 0) {
                                        printf("Folgender Fehler ist aufgetreten: ");
                                        printf("%s\n", Buffer);
                                        break;
                        }
                       
                        if (strstr(Vergleich, "+ WAIT") != NULL) {
                            send(sock, Warten, strlen(Warten), 0);
                            printf("%s\n", Vergleich);
                            bzero(Vergleich, BIGBUF);
                        }
                       
                        if (strstr(Vergleich, "+ ENDPIECELIST") != NULL  && (strstr(Vergleich, "+ QUIT") == NULL)) {

								Spielstein_struct_Array = Spielstein_Zeiger;
   
                          char *Ausgabezeile = (char*) malloc(sizeof(char)* 356);  
                          char *Variable =  (char*) malloc(sizeof(char)*2);
                          char Sep1[] = "\n";
                          char Sep2[] = " \n";
                          int i = 0;                            
                         
                          bzero(Variable,3);
                          bzero(Ausgabezeile,BUF+1);
                         
                         
                          send(sock, Denken, strlen(Denken ), 0);
                         
                          // Auslesen der Zugezeit
                         strcpy(Ausgabezeile,Vergleich);                          
                         Ausgabezeile = strstr(Ausgabezeile,"MOVE");
                         Ausgabezeile = strtok(Ausgabezeile,Sep2);
                         Ausgabezeile = strtok(NULL,Sep2);
                         printf("Zugzeit: %s ms\n", Ausgabezeile);
                         
                         //Auslesen der schlagbaren Steine
                         strcpy(Ausgabezeile,Vergleich);
                         Ausgabezeile = strstr(Ausgabezeile,"CAPTURE");
                         Ausgabezeile = strtok(Ausgabezeile,Sep2);
                         Ausgabezeile = strtok(NULL,Sep2);
                         Info_struct->capture = (int) *Ausgabezeile - 48;
                         printf("Schlagbare Steine: %d\n", Info_struct->capture);
                         
//                       Auslesen der Anzahl der Spieler und Steine
                         strcpy(Ausgabezeile,Vergleich);
                         Ausgabezeile = strstr(Ausgabezeile,"PIECELIST ");
                         Ausgabezeile = strtok(Ausgabezeile,Sep2);
                         Ausgabezeile = strtok(NULL,Sep2);
                         printf("Anzahl Spieler, Anzahl Steine: %s\n", Ausgabezeile);
                         
//                       Auslesen aller Steinpositionen
                         for (i=0; i<18;i++) {
                         Ausgabezeile = strtok(NULL,Sep1);
                         
//                       Auslesen der Spielernummer
                         Variable = strncpy(Variable , Ausgabezeile + 7, 1);
                         Spielstein_struct_Array[i].Spielernummer = (int)*Variable-48;
                         bzero(Variable,2);
                         
//                       Auslesen der Steinposition
                         Variable = strncpy(Variable , Ausgabezeile + 11,strlen(Ausgabezeile)-11);                      
                         strcpy(Spielstein_struct_Array[i].Position,Variable);
                         Spielstein_struct_Array[i].Flag=1;
                         }
                         
                         free(Variable);
                         
                         bzero(Vergleich, BIGBUF);
                           
                        }
                       
                        if (strstr(Vergleich, "+ OKTHINK") != NULL) {                           // In diesem If-case wird ein MOVE Befehl vom Server erwartet
                        printf("%s\n",Vergleich);

            int Signal_geben;
            Signal_geben=kill(getppid(), SIGUSR1);
            if (Signal_geben==-1) {
                perror("Fehler beim Signalerzeugung");
            }
            bzero(Lesepuffer, 10);

                

                        FD_ZERO(&Filedeskriptoren);
                        FD_SET(Info_struct->fd0, &Filedeskriptoren);
                        FD_SET(sock, &Filedeskriptoren);

                if (select (FD_SETSIZE, &Filedeskriptoren, NULL, NULL, NULL) < 0) {
                        perror ("Fehler bei select.");
                        break;
                        }

                       

                        // Timeout behandeln.
                        if (FD_ISSET(sock, &Filedeskriptoren) != 0) {
                                bzero(Buffer, BUF);
                                recv(sock, Buffer,BUF,0);

                                if (strcspn(Buffer, "-") == 0) {
                                        printf("Server meldet Timeout.\n");
                                        break;
                                }
                        }


                        // Spielzug steht zur Verfügung.
                        else {
                                if (read(Info_struct->fd0, Lesepuffer, 10) < 0) {
                                        perror ("Fehler bei read.");
                                }
                                bzero(Spielzug, 100);
                                strcpy(Spielzug, "PLAY ");
                                strcat(Spielzug, Lesepuffer);                                                                                          
                                strcat(Spielzug, "\n");
                               
                                send(sock,Spielzug, strlen(Spielzug),0);
                        }

                        bzero(Vergleich, BIGBUF);
                    }
                       
                          if (strstr(Vergleich, "+ QUIT") != NULL) {                            // Herausfiltern des Gewinners nach dem selben
                           char Nadel[] = "+ GAMEOVER";
                           char *Gameover_Zeile = strstr(Vergleich, Nadel);                     // Prinzip wie bei performConnection
                           char *Gewinner_Nummer= (char*) malloc(sizeof(char)*BIGBUF);          
                           char Seperator[] = "\n";
                           
                           Gameover_Zeile = strtok(Gameover_Zeile, Seperator);
                           
                            if (strlen(Gameover_Zeile) > GAMEOVER_OFFSET+1) {                   // Nur wenn es einen Gewinner gibt
                              printf("gewinner erreicht\n");
                              strncpy(Gewinner_Nummer, Gameover_Zeile + GAMEOVER_OFFSET, strlen(Gameover_Zeile) - GAMEOVER_OFFSET);
                              printf("%s\n", Gewinner_Nummer);
                            }
                           
                            else {                                                              // Im Falle eines Unentschieden
                              printf("Unentschieden!\n");
                                printf("%s\n", Vergleich);
                            }
                           
                            free(Gewinner_Nummer);
                            break;
                          }
                }

                free(Buffer);
                free(Eingabe);
                free(Lesepuffer);
                free(Spielzug);
                free(Vergleich);
                return 0;
}


